home *** CD-ROM | disk | FTP | other *** search
/ TeX 1995 July / TeX CD-ROM July 1995 (Disc 1)(Walnut Creek)(1995).ISO / macros / latex209 / contrib / chbars / chbars.doc (.txt) < prev    next >
LaTeX Document  |  1993-01-11  |  36KB  |  796 lines

  1. % This is CHBARS.STY                      as of Aug '92
  2. % -*-LaTeX-*-
  3. %---------------------------------------------------------
  4. % (c) 1989 by J.Schrod. copy conditions see below.
  5. % Macro package for creating changebars in LaTeX.
  6. % MAKEPROG will ``weave'' this file into documentation that can be LaTeX'ed.
  7. % documented in LaTeX (for Anne and Chris)
  8. %       VERSION HISTORY  (MSCF -- most significant change first)
  9. % DATE     PERSON  REMARK
  10. % 92-08-15 -rlb    Use change bars in this document to mark major changes;
  11. %                  so now you can see for yourself what they are.
  12. % 92-07-28 -rlb    Run through a spelling checker before distributing.
  13. % 92-01-15 -rlb    1) Keep \maxdeadcycles the same; just 
  14. %                     don't count calls to output for change processing. 
  15. %                  2) Allow setting the change bar width.
  16. %                  3) Some typos and rewording of some of the comments.
  17. %                  4) Allow setting change bar width. Interface copied
  18. %                     from changebars.sty
  19. %                  5) Allow changebars to be on the left as well as
  20. %                     the right.
  21. %                  6) localize some variables global definitions now
  22. %                     start with cb_.
  23. %                  7) Chain onto existing \output routine rather than assume
  24. %                     \plainoutput.
  25. %                  And miscellaneous minor hacks.
  26. % 89-10-09 -js     converted to LaTeX (progltx)
  27. % 89-09-25 -js     repaired \mark processing in horizontal mode
  28. % 89-08    -js     first version (for EuroTeX89 in Karlsruhe)
  29. % author's current address:
  30. %       Detig$\,\cdot\,$Schrod \TeX{}sys
  31. %       Joachim Schrod
  32. %       Kranichweg 1
  33. %       D-6074 R\"odermark-Urberach
  34. %       FR Germany
  35. %       Tel. (+6074) 1617
  36. %       Bitnet: XITIJSCH@DDATHD21
  37. % should be progtex...
  38. % These TeX macros were documented with the documentation system
  39. % MAKEPROG and automatically converted to the current form.
  40. % If you have MAKEPROG available you may transform it back to
  41. % the original input: Remove every occurence of three percents
  42. % and one optional blank from the beginning of a line and remove
  43. % every line which starts with four percents.  The following lex
  44. % program will do this:
  45. %    %%
  46. %    ^%%%\ ?   ;
  47. %    ^%%%%.*\n ;
  48. % MAKEPROG may be obtained over the net from the Bitnet-Listserver
  49. % LISTSERV@DHDURZ1 (filelist WEBWARE), from tuglib@science.utah.edu,
  50. % or via ftp from june.cs.washington.edu.
  51. \documentstyle[progltx,chbars,a4-9]{article}
  52. % local macros
  53. \let\mc=\small              % for names like GNU
  54. \def\PS{{\sc PostScript}}
  55. \def\DVI{{\tt DVI}}
  56. \def\GNU{{\mc GNU}}
  57. \chardef\bs=`\\
  58. \begin{document}
  59. \tableofcontents
  60. \title{Changebars without {\tt \bs{}special}'s}
  61. \author{\sc Joachim Schrod}
  62. \newcommand{\changedate}{Aug 15, 1992}
  63. \date{Revised last on \changedate\footnote{by {\tt rocky@watson.ibm.com}}\\ 
  64.       Formatted on \today}
  65. \maketitle
  66. \begin{abstract}
  67. It is common practice to use vertical bars in the margins of a
  68. document to mark pieces of text which have changed since the last
  69. version(s) of this document. Such vertical bars are usually
  70. called {\em changebars}. It has often been said that it is
  71. impossible to produce changebars with \TeX{} without the usage of
  72. |\special| commands (driver directives), which extend the
  73. primitives of \TeX{}. This paper presents a \TeX{} macro file
  74. which implements changebars without such a usage. The macro file
  75. is written for the usage with {\sc Plain}~\TeX{} but the implementation 
  76. strategy can be used with \LaTeX{}, too with minor changes.
  77. \end{abstract}
  78. \chap Introduction.
  79. Changebars are used to mark modified parts in existing documents.  For
  80. the usage in \TeX{} documents, there exist only solutions that use
  81. driver/printer features by the way of inserting |\special| commands in
  82. the \TeX{} source, e.g.\ for \PS{} drivers.  This results in documents
  83. that are no longer as freely interchangeable as the \DVI{} concept would
  84. allow---device dependency is problematic especially for this application
  85. that is useful for multi-authoring or standards development.
  86. This macro package offers a pure \TeX{} solution.  Nevertheless, it has
  87. its restrictions, too. The page break will no longer be optimal,
  88. because there is no strechability or shrinkability of a page 
  89. on top of the last
  90. region of change marked. But this seems to be acceptable,
  91. especially as the change bar feature often will be used for proof
  92. reading and not in the final document. This restriction is the reason
  93. why no change marks can be used on title pages or on similar
  94. constructions. Changes in floating insertions (footnotes, figures) are
  95. not handled. Multi-column formats such as a two-column layout
  96. will not work. There is currently no support for nested changes.
  97. The method for writing a change bar consists of three
  98. parts: First, the output routine is signalled when the beginning
  99. or end of a change bar setting command is encountered.
  100. Next, the position of the changed area is found out
  101. and fixed; the end-of-change command adds the last change bar
  102. position, length and width to a list of all such positions,
  103. lengths and widths of change bars that is accumulated for the current 
  104. page. Finally, when
  105. the output routine is triggered (either asynchronously in trying
  106. to ship out the page or synchronously when discovering that the
  107. end of a change bar lies off the page), this list of change
  108. bars information is used create vertical rules which are added to the page.
  109. If the change bar is not complete, a ``virtual'' end change is
  110. inserted, and a ``virtual'' begin change is inserted at the
  111. beginning of the next page.
  112. \beginchange
  113. The demonstrated solution was originally written in {\sc Plain}
  114. \TeX{}, because it was
  115. easier and could be presented better at the Euro\TeX89 conference in
  116. Karlsruhe.  An adaptation to \LaTeX{} has been done too which
  117. requires minor modifications to the \LaTeX's output routine.
  118. \endchange
  119. Some history. The routines were initially written by Joachim
  120. Schrod. Around Jan.\ 1992, R.\ Bernstein added some of the features
  121. coded in |changebars.sty| to combine the best features
  122. of the two (and added a couple of his own). For example, the
  123. ability to specify change bar widths, put the change bars either on the
  124. right or left margin, specifiy the distance from the margin to the
  125. change bar, and chain to on top of a pre-existing modified 
  126. output routine. 
  127. \beginchange
  128. Initially, both the {\TeX} and the {\LaTeX}
  129. versions were put into one file. However, due problems in
  130. dealing with conditional definition of code, in particular
  131. problems with an extra or omitted |\fi| in defining or not
  132. |\ifr@ggedbottom|, the code was split into two. 
  133. \endchange
  134. The |changebars.sty| package
  135. was written by Michael Fine and revised by Johannes Braams
  136. and Neil Winton. One or two ideas from Thomas J.~Reid have been
  137. used.
  138. \chap GNU General Public License.
  139. This program is free software; you can redistribute it and/or
  140. modify it under the terms of the \GNU{} General Public License as
  141. published by the Free Software Foundation; either version~2, or (at your
  142. option) any later version.
  143. This program is distributed in the hope that it will be useful, but
  144. {\bf without any warranty\/}; without even the implied warranty of
  145. {\bf merchantability\/} or {\bf fitness for a particular purpose}.  See
  146. the \GNU{} General Public License for more details.
  147. You should have received a copy of the \GNU{} General Public License
  148. along with this program; if not, write to the Free Software Foundation,
  149. Inc., 675~Mass Ave, Cambridge, MA~02139, USA.
  150. \chap User Interface.
  151. \begin{newsloppy}
  152. A changed area is described by two marks, |\beginchange| and
  153. |\endchange|. The |\beginchange| can take an optional argument,
  154. the bar thickness, enclosed in brackets. That is, either of the
  155. something like |\beginchange[0.4pt]|, or |\beginchange| is valid. 
  156. In the latter case where no thickness dimension is specified, the value
  157. of |\changebarwidth| is used. Therefore, by setting this you can
  158. change the bar thickness used when none is given. Changing the bar
  159. thickness might be useful in identifying different classes of changes.
  160. Another pair of global values that can be set are the 
  161. logical variables 
  162. {\tt \bs chbar\-Right\-true} and {\tt \bs chbar\-Right\-false}. 
  163. These specify whether
  164. changebars should be on the right or left. Say, if one wanted to
  165. have the changebars always appear on the outside margin, one could
  166. change these accordingly on each page.
  167. \end{newsloppy}
  168. Finally |\BarDistance| is the amount of place between the text margin
  169. and the change bars. This value should normally be positive. For
  170. change bars on the right this value is added to |\hsize|, while
  171. for change bars on the left this value is {\em subtracted\/} from
  172. |\hoffset|.
  173. \beginprog
  174. \newdimen\changebarwidth \changebarwidth=1pt\relax
  175. \newif\ifchbarRight \chbarRightfalse
  176. \newdimen\BarDistance  \BarDistance=2cc
  177. \endprog
  178. \chap Utility Routines and Programming Conventions.
  179. Before we get into the nitty gritty details, we give some common
  180. macros. 
  181. First, we declare some shorthands for category codes.  By
  182. declaring the at sign~(`|@|') as well as the underscore~`(|_|)' as
  183. letters we can use them in our macros. (I agree with D.~Knuth that
  184. |\identifier_several_words_long| is more readable than
  185. |\IdentifierSeveralWordsLong| and in every case better than |\p@@@s|.)
  186. \beginchange
  187. By defining the at sign to be in the letter class,
  188. we can access {\sc Plain} \TeX's ``private''macros. By defining the
  189. underscore to be in the letter class, we make our own private macros more 
  190. readable. But since have to
  191. restore these category codes at the end of this macro file, we store
  192. \endchange
  193. their former values in the control sequences |\atcode| and |\uscode|.
  194. This method is better than to use a group because not all macros have to
  195. be defined global this way.
  196. All {\em global\/} definitions of the package are prefixed by
  197. |cb_|. In this way, they can be easily determined. If other
  198. packages do likewise, it is less likely that definition
  199. names will clash between packages. (Unless the other package uses the
  200. |cb_| prefix too, in which case there is a good chance that many
  201. variable names will clash.)
  202. The {\TeX}book recommends the 255 registers be used for scratch
  203. space, so routines use this value when a spare register is
  204. needed. (See for example |\cb_write_bar|.)
  205. \beginprog
  206. \chardef\letter=11
  207. \chardef\atcode=\catcode`\@
  208. \chardef\uscode=\catcode`\_
  209. \catcode`\@=\letter
  210. \catcode`\_=\letter
  211. \endprog
  212. \sect Now we are ready to code the top-level routine for indicating the 
  213. beginning of a change. But first we display the banner and
  214. version number associated with this package. If this package has
  215. been loaded already we terminate.
  216. Although one can specify a bar
  217. thickness on the |\beginchange| macro, it is at the |\endchange|
  218. where this thickness is recorded and put on a current list of
  219. change bar entries for the current page. It seems more natural to 
  220. specify the thickness at the beginning of the change rather than
  221. the end. Therefore at the
  222. |\beginchange| we merely save the value given or save the value of
  223. |\changebarwidth| if no thickness was given.
  224. |\cb_changebarwidth| is this saved value. Auxiliary routine
  225. |\cb_xstart| is used to parse off the optional enclosing
  226. brackets. (This weird name was taken from one used in |changebars.sty|.)
  227. default value, that is the value when a width is not specified 
  228. is |\changebarwidth|.
  229. \beginprog
  230. \@ifundefined{cb_xstart}{}{\endinput}
  231. \typeout{Style option `change bars' -- version 1.1a}
  232. \newdimen\cb_changebarwidth
  233. \def\beginchange{\@ifnextchar [
  234.                     {\cb_xstart}
  235.                     {\beginchange_\changebarwidth}}
  236. \def\cb_xstart[#1]{\beginchange_{#1}}
  237. \endprog
  238. \chap Triggering the Output Routine.
  239. \beginchange
  240. When a change bar is started or completed, we signal the output
  241. routine in the ``usual'' way described below. Why bother the
  242. output routine? The output routine is supposed to be thought of 
  243. as an asynchronous process which has access to the page via the
  244. contents of box register 255. In theory, if we did not perform
  245. actions in the output routine, it might get triggered inadvertantly,
  246. and the output routine might modify this box register on us in the
  247. midst of our work. The information that needs to be recorded
  248. is the position of the beginning and end of the lines containing a
  249. marked change region.
  250. \begin{newsloppy}
  251. The macros |\beginchange| and |\endchange| signal the output
  252. routine. Rather than merely calling the output routine directly,
  253. these routines signal the output routine by setting a low page penalty, i.e.,
  254. one which tells {\TeX} that this is a really good place to break
  255. the page. Of course, we will modify the output routine so that it
  256. doesn't really split the page when it has been called in this fashion.
  257. Again, the reason we signal the output routine in what seems at
  258. first a pretty odd way, is that this is the way it is supposed to
  259. be done. If a low penalty were not set, some 
  260. other action (if we weren't careful) might cause a penalty to be 
  261. set and thus call the output routine (recursively).
  262. So that we can distinguish our calls from others, |\beginchange|
  263. and |\endchange| reserve a range of 
  264. penalty values; he actual value is stored in |\cb_break_penalty|.
  265. This range of values is below 
  266. $-10\,000$, the nominal value for indicating that a page should
  267. occur now. The penalty will be in the range $|\cb_penalty_group|
  268. \cdot 100 - 99 \ldots |\cb_penalty_group| \cdot 100$.
  269. The output routine then decides whether it has been called 
  270. to start or end a change bar or neither.  This is done with the values
  271. |\cb_penalty_begin| and |\cb_penalty_end| that are used as the
  272. second-to-last digit of the change penalty.
  273. \end{newsloppy}
  274. The output routine also needs to determine whether
  275. the beginning of change bar starts in horizontal or vertical mode
  276. so it can figure out the exact placement for the beginning of the
  277. bar line. In horizontal mode, the change bar does
  278. not begin at the baseline of the actual text position, but on top of the
  279. actual line.  This is marked in the last digit, an odd digit will be
  280. used in horizontal mode.
  281. Note that the values mentioned above are used as digits here that can be
  282. concatenated.  If they are not followed by an other digit they should be
  283. terminated by |\space| to stop {\TeX}'s look-ahead scanning for
  284. digits when reading a number. 
  285. \endchange
  286. \beginprog
  287. \def\cb_penalty_group{-101} \def\cb_penalty_begin{0}
  288. \def\cb_penalty_end{1}
  289. \endprog
  290. \sect The calls to start or end a change bar, set an encoded page-break
  291. penalty which includes an
  292. indication of begin or end of a change bar. The variable
  293. |\cb_break_penalty| is used to create such as special value.
  294. The rest (|\cb_trigger_output|) is the same action
  295. for both. The region just before the end of the changed region can be 
  296. in horizontal mode and preceded by
  297. glue that could cause a line break, thus including the following line to
  298. the change area as well.  To avoid this unwanted behavior, the space is
  299. saved in |\save_lastskip|, discarded in front of the mark and restored
  300. afterwards. 
  301. \beginprog
  302. \newcount\cb_break_penalty
  303. \def\beginchange_#1{%
  304.    \cb_changebarwidth=#1
  305.    \cb_break_penalty=\cb_penalty_group\cb_penalty_begin0
  306.    \cb_trigger_output
  307. \def\endchange{{%
  308.    \skipdef\save_lastskip=255%
  309.    \ifhmode \save_lastskip=\lastskip \unskip \fi%
  310.    \cb_break_penalty=\cb_penalty_group\cb_penalty_end0%
  311.    \cb_trigger_output%
  312.    \ifhmode \hskip\save_lastskip \fi%
  313.    }}%\endchange
  314. \endprog
  315. \sect The next routine, |\cb_trigger_output|,
  316. triggers calls to the output routine by setting the page-break 
  317. penalty, |\penalty|. {\TeX} may discard an |\output| invocation at
  318. the beginning of a page.  So we trigger the output routine twice, first
  319. with a special penalty value that is 2~less than the correct value
  320. (including the code for horizontal or vertical mode).  After the first
  321. page break, it is asserted that the current list is empty.  The output
  322. routine has to save the former page contents if necessary.
  323. Next we set the penalty to the correct value.  The second page break does
  324. the real work, restores the page contents and handles the split
  325. insertions (footnotes, figures,~\dots). 
  326. In horizontal mode |\spacefactor| must not be destroyed, so it is
  327. saved and restored via local count register |\save_spacefactor|.
  328. \beginprog
  329. \def\cb_trigger_output{%
  330.    \ifinner \errmessage{Change cannot be marked inside a box}%
  331.    \else{%
  332.      \countdef\save_spacefactor=255%
  333.      \ifvmode   
  334.        \let\do_in_vmode=\relax
  335.        \advance \cb_break_penalty by -2
  336.      \else 
  337.        \save_spacefactor=\spacefactor
  338.        \let\do_in_vmode=\vadjust
  339.        \advance \cb_break_penalty by -3
  340.      \fi
  341.      \do_in_vmode{%
  342.        \penalty\cb_break_penalty%        first call to \output
  343.        \null
  344.        \advance \cb_break_penalty by 2 
  345.        \penalty\cb_break_penalty%        second call to \output
  346.        }%
  347.      \ifhmode \spacefactor=\save_spacefactor \fi
  348.      }%
  349.    \fi
  350.    }% cb_trigger_output
  351. \endprog
  352. \sect Using the output routine for passing
  353. information has it's difficulties.  One of the hard parts is 
  354. \beginchange
  355. handling page marks. These are token lists which are set by the
  356. |\mark| command. They record information that can later 
  357. be accessed by the output routine. The canonical example of such
  358. a use of the |\mark| command is in creating 
  359. \endchange
  360. dictionary-style entry headings.
  361. The output routine can access one of three 
  362. token lists through three control sequences:  |\botmark| is the
  363. last page mark given, |\topmark| is the |\botmark| of the previous
  364. page, and |\firstmark| is the first page mark on the actual page or
  365. |\topmark| if none was given.  Here ``page'' is used in the \TeX{}
  366. sense, i.e.\ as the material which has been collected between two
  367. |\output| invocations. Of course, the page marks must not be
  368. destroyed---and that means they must be reinserted after each
  369. special use of the output routine.
  370. But we are lucky: A ``special use'' consists of two |\output|
  371. invocations, so we can insert |\topmark| again as a page mark after
  372. the first invocation where it will be the only page mark on that
  373. \TeX{} page.  The second invocation will automatically transform this
  374. page mark into the ``last page mark on the previous page,'' i.e.\ in
  375. |\topmark|---that's what we need!  Furthermore |\firstmark| and
  376. |\botmark| are saved in control sequences during the first invocation,
  377. they will be inserted again, too.
  378. There's one situation where this approach doesn't work: in front of
  379. the first page mark. Here, |\topmark|, |\firstmark|, and |\botmark| expand
  380. to an empty token list. If we save them then and insert their old
  381. values we have inserted empty page marks. If other page marks follow
  382. on the same ``real'' page, |\firstmark| will be empty instead of
  383. expanding to the token list of the first page mark.  To prevent this
  384. we must not save and restore page marks before the first |\mark| has
  385. been added to the main vertical list.
  386. Well, that can be controlled with a switch---but this switch must be
  387. set very carefully.  If it is set immediately by the first |\mark|
  388. this may be in horizontal mode and special output invocations can
  389. occur above this page mark (i.e., there may be a |\beginchange| in the
  390. same paragraph in front of the |\mark|).  Therefore the setting of the
  391. switch must be delayed until the vertical position of the |\mark|
  392. (precisely:  the position of the |\mark| in the current list) is
  393. reached.  In horizontal mode this can be done with a |\vadjust| and
  394. the output routine! Voil\`a, this is another command group for the
  395. output routine with only one command.
  396. \beginprog
  397. \newif\if_cb_save_mark@  \_cb_save_mark@false
  398. \def\mark_penalty_group{-102}
  399. \endprog
  400. \sect We will redefine |\mark| so that the first page mark either sets
  401. the switch to true (in vertical mode all possible special page breaks
  402. are already handled) or forces the |\output| routine to do this at an
  403. appropriate place. In the last case we can use |\cb_trigger_output|
  404. again. Afterwards we restore the original meaning of |\mark| again to
  405. reduce the processing overhead (and the dead cycles).
  406. This change of |\mark| has the consequence that the first |\mark| in a
  407. document cannot be used anymore in horizontal mode inside a vertical
  408. box that shall be split afterwards.  But this is only sensible if this
  409. mark shall be used as |\splitfirstmark| because it will almost never
  410. migrate to the outer list---really a rare case!
  411. \beginprog
  412. \let\cb_mark=\mark
  413. \def\mark{%
  414.    \ifvmode
  415.      \ifinner \else  \global\_cb_save_mark@true \fi % split marks!
  416.    \else  
  417.      \cb_break_penalty=\mark_penalty_group00 % this will corrupt \vsplit
  418.      \cb_trigger_output
  419.    \fi
  420.    \global\let\mark=\cb_mark
  421.    \cb_mark
  422. \endprog
  423. \sect 
  424. \begin{newsloppy}
  425. If the output routine is triggered with the mark penalty value,
  426. it will call {\tt \bs cb\_save\discretionary{\_}{}{}page\_marks}.
  427. \end{newsloppy}
  428. \beginprog
  429. \def\cb_save_page_marks{%            % this may be executed twice
  430.    \unvbox\@cclv
  431.    \global\_cb_save_mark@true
  432. \endprog
  433. \sect To finish the treatment of page marks we can formulate the two
  434. macros which are used at the first resp.\ second invocation of a
  435. ``special output,'' the principles have already been explained.
  436. \beginprog
  437. \def\cb_backup_page_marks{%
  438.    \if_cb_save_mark@
  439.       \mark{\topmark}%
  440.       \xdef\cb_save_firstmark{\firstmark}%
  441.       \xdef\cb_save_botmark{\botmark}%
  442.    \fi
  443. \def\cb_restore_page_marks{%
  444.    \if_cb_save_mark@
  445.       \mark{\cb_save_firstmark}\mark{\cb_save_botmark}%
  446.    \fi
  447. \endprog
  448. \chap Positioning the Change Bars.
  449. \begin{newsloppy}
  450. Now we handle the positions of the bars.  
  451. The dimension |\cb_bot_change_pos| will hold
  452. the position of the end of the change bar, i.e.\ the distance between 
  453. top of page the end of the changed area. The dimension
  454. |\cb_top_change_pos| will hold the beginning of a changed
  455. area; a value of |\maxdimen| indicates that no change is in effect.  If
  456. a changed area is completed, it is appended to the list |\cb_bar_list| as
  457. an element {\tt \bs cb\_bar(\bs cb\_top\_change\_pos,
  458. \bs cb\_bot\_change\_pos, \bs changebar\-width)}.  This list contains all
  459. changed areas within the current page so that bars can be written later
  460. on.  A single bar will be produced by |\cb_write_bar|.
  461. \noindent The definition of |\cb_bar| to |\relax| allows the concatenation
  462. of new elements to |\cb_bar_list| with |\xdef|.
  463. Local dimension register |\halfwidth| is used to center the change bar.
  464. \end{newsloppy}
  465. \beginprog
  466. \newdimen\cb_bot_change_pos
  467. \newdimen\cb_top_change_pos  \cb_top_change_pos=\maxdimen
  468. \let\cb_bar_list=\empty
  469. \let\cb_bar=\relax
  470. \def\cb_write_bar(#1,#2,#3){{%
  471.    \dimendef\halfwidth=255%
  472.    \setbox0=\hbox{\vrule width #3 height -#1  depth #2}%
  473.    \dp0=0pt \ht0=0pt \wd0=0pt%
  474.    \halfwidth=#3 \divide\dimen255 by 2%
  475.    \hskip -\halfwidth%
  476.    \box0%
  477.    \hskip \halfwidth%
  478.    }}
  479. \endprog
  480. \sect If the output routine was activated by a |\outputpenalty| value
  481. within the range of our reserved penalties, the change handling will
  482. occur, otherwise standard plain output can be done. There may be
  483. a lot of interaction between the routines which set change bars and
  484. the output routine. These interactions should not be recorded in
  485. |\deadcycles| or else \TeX{} will soon grumble. One might consider
  486. doing the same for the|\mark_penalty_group|. But since this 
  487. group is called once it shouldn't matter all that much.
  488. One might also consider adding a counter like |\deadcycles| just
  489. to count the change bar interactions as is done for the calls to
  490. |\output|, and have {\TeX} grumble if there are ``too many'' of
  491. them. For now we don't do this---all of this code is correct
  492. anyway!
  493. First we save away the old output routine in case the user had redefined
  494. this beforehand.
  495. \beginprog
  496. \newtoks\cb_oldoutput
  497. \edef\cb_oldoutput{\the\output}
  498. \newcount\penalty_group
  499. \output={%
  500.    \boxmaxdepth=\maxdepth
  501.    \penalty_group=\outputpenalty  \divide \penalty_group by 100
  502.    \ifnum \penalty_group=\cb_penalty_group\space  
  503.      {\countdef\new_deadcycles=255% don't count as a dead cycle
  504.       \new_deadcycles=\deadcycles       
  505.       \advance \new_deadcycles by -1
  506.       \deadcycles=\new_deadcycles}
  507.      \cb_change_handling
  508.    \else
  509.       \ifnum \penalty_group=\mark_penalty_group\space
  510.          \cb_save_page_marks
  511.       \else \cb_oldoutput
  512.       \fi
  513.    \fi
  514. \endprog
  515. \sect As explained before, the change handling must differentiate
  516. between the kind of the change command (beginning is indicated by
  517. $|\cb_change_cmd|=0$, end by~1) and between the mode (horizontal indicated
  518. by an odd |\cb_change_mode| value, vertical by an even).  A change mode
  519. higher than one indicates that we are doing the first page break that
  520. has to backup the page as far as it exists already and results in an
  521. empty current list of page elements.
  522. \beginprog
  523. \newcount\cb_change_cmd
  524. \newcount\cb_change_mode
  525. \def\cb_change_handling{%
  526.    \cb_change_cmd=-\outputpenalty                  % ==> absolute value
  527.    \advance \cb_change_cmd by \cb_penalty_group00  % subtraction
  528.    \cb_change_mode=\cb_change_cmd
  529.    \divide \cb_change_cmd by 10                    % second-to-last digit
  530.    \advance \cb_change_mode by -\number\cb_change_cmd0   % last digit
  531.    \ifnum \cb_change_mode>1  \cb_backup_page
  532.    \else
  533.       \ifcase \cb_change_cmd  \cb_begin_change
  534.       \or \cb_end_change
  535.       \else \errmessage{Invalid changepenalty}%
  536.       \fi
  537.    \fi
  538. \endprog
  539. \sect Processing a mark during the second trigger of the output routine
  540. means restoring the page and storing the positions.  At the beginning,
  541. the begin of the change is saved, at the end, we know the bar already
  542. and put it into the bar list.  Then the positioning values are
  543. reinitialized.
  544. As within every output invocation, the box 255 must be unboxed.  As we
  545. are here in the second invocation of the output routine the |\box255|
  546. consists only of the empty |\vbox| we have inserted in
  547. |\cb_trigger_output|.  We can therefore throw it away.
  548. \beginprog
  549. \def\cb_begin_change{%
  550.    \cb_restore_page
  551.    \setbox0=\box\@cclv
  552.    \ifdim \cb_top_change_pos=\maxdimen
  553.       \global\cb_top_change_pos=\cb_bot_change_pos  
  554.       \global\cb_bot_change_pos=0pt
  555.    \else \errmessage{Nested change bars are not supported}%
  556.    \fi
  557. \def\cb_end_change{%
  558.    \cb_restore_page
  559.    \setbox0=\box\@cclv
  560.    \ifdim \cb_top_change_pos=\maxdimen
  561.       \errmessage{No change is in effect}%
  562.    \else
  563.       \xdef\cb_bar_list{\cb_bar_list 
  564.          \cb_bar(\the\cb_top_change_pos,\the\cb_bot_change_pos,
  565.                  \the\cb_changebarwidth)}
  566.       \global\cb_top_change_pos=\maxdimen
  567.       \global\cb_bot_change_pos=0pt
  568.    \fi
  569. \endprog
  570. \chap Handling the Page Contents.
  571. We handle the part of the page that was collected up to now by putting
  572. it into a box. This fixes the position of the change mark so that
  573. |\cb_bot_change_pos| can be set and stored in |\cb_top_change_pos|
  574. later on or as the lower end of a bar in |\cb_bar_list|.
  575. In the first output invocation, we save the contents of the 
  576. page in |\cb_save_page|. Before that, we store the size of the
  577. box (which equals |\pagegoal|!)\ in |\cb_page_goal|.  If the unboxing
  578. caused an increase of height (i.e.\ if $|\pagetotal|>|\pagegoal|$),
  579. we eject the page up to the change mark.  Now we have to compute the
  580. current position of our mark in |\cb_bot_change_pos|. It is fixed by the size
  581. of the |\cb_save_page|, but if the change begins in horizontal mode we
  582. must decrease it from the baseline position to the top of the last line.
  583. Finally, we must save the values for the allowed insertions and change
  584. them to the maximal value so that a rest that is split from an insertion
  585. will be appended to the insertion box at the second invocation in every
  586. case.
  587. The |\vsize| is initialized to |\maxdimen|.  This allows to control
  588. whether this first output invocation ocurred or if it was discarded.
  589. For the same reason |\cb_bot_change_pos| is initialized to~0pt.
  590. \beginprog
  591. \newbox\cb_save_page
  592. \newdimen\cb_page_goal
  593. \newdimen\cb_save_vsize         \cb_save_vsize=\maxdimen
  594. \newdimen\cb_save_dimen_topins
  595. \newdimen\cb_save_dimen_footins
  596. \def\cb_backup_page{%
  597.    \global\cb_page_goal=\ht\@cclv
  598.    \global\setbox\cb_save_page=\vbox{\unvbox\@cclv}%
  599.    \ifdim \ht\cb_save_page>\cb_page_goal  
  600.       \cb_eject_page_so_far \fi
  601.    \cb_bot_change_pos=\ht\cb_save_page  
  602.    \global\advance \cb_bot_change_pos by \dp\cb_save_page
  603.    \ifnum \cb_change_cmd=\cb_penalty_begin\space
  604.       \ifodd \cb_change_mode \higher_change_pos \fi
  605.    \fi
  606.    \global\cb_save_vsize=\vsize  \global\vsize=\maxdimen
  607.    \global\cb_save_dimen_footins=\dimen\footins
  608.    \global\dimen\footins=\maxdimen
  609.    \cb_backup_page_marks
  610. \cb_bot_change_pos=0pt
  611. \endprog
  612. \sect To eject a page as far as it is we restore it from the
  613. |\cb_save_page| back to box~255.  In horizontal mode and at a begin mark
  614. the last line contains the mark and must not be output.  So we remove it
  615. and the preceding glue from the stored rest, just leaving a single hbox
  616. to be on top of the actual page (in |\cb_save_page|) now. Then normal
  617. output can be done with box~255.
  618. \beginprog
  619. \def\cb_eject_page_so_far{%
  620.    \begingroup
  621.      \vbadness=20000 % don't complain about underfull vboxes
  622.      \global\setbox\@cclv=\vbox to \cb_page_goal{%
  623.          \unvbox\cb_save_page
  624.          \ifnum \cb_change_cmd=\cb_penalty_begin\space
  625.             \ifodd \cb_change_mode
  626.                \global\setbox\cb_save_page=\lastbox
  627.                \unskip
  628.             \fi
  629.          \fi
  630.          }%
  631.    \endgroup
  632.    \cb_oldoutput
  633. \endprog
  634. \sect 
  635. \begin{newsloppy}
  636. In horizontal mode and at a begin mark, we need the position of
  637. the mark ({\tt \bs cb\_bot\discretionary{\_}{}{}change\_pos}) on the upper boundary of 
  638. the last line in
  639. |\cb_save_page|.  If there is just one line left from a recent eject, the
  640. height is given by |\topskip| decreased by the height of this hbox.  If
  641. the height of the box is larger than |\topskip| the skip will not be
  642. inserted and the change position results to~0pt. Otherwise,
  643. |\cb_save_page| is a vbox whose last hbox we delete temporarily using
  644. box~0. Height and depth of the rest are the actual position on the page.
  645. \end{newsloppy}
  646. The double of the page we have constructed this way will immediately be
  647. fed back to the garbage collector because it could have become
  648. reasonably large.
  649. \beginprog
  650. \def\higher_change_pos{%
  651.    \ifhbox \cb_save_page % rest of page from \cb_eject_page_so_far
  652.       \cb_bot_change_pos=\topskip  
  653.       \global\advance \cb_bot_change_pos by -\ht\cb_save_page
  654.       \ifdim \cb_bot_change_pos<0pt  
  655.          \global\cb_bot_change_pos=0pt  
  656.       \fi
  657.    \else
  658.       \setbox0=\vbox{%
  659.          \unvcopy\cb_save_page
  660.          \setbox0=\lastbox % delete last line
  661.          }%
  662.       \cb_bot_change_pos=\ht0   
  663.       \global\advance \cb_bot_change_pos by \dp0
  664.       \setbox0=\box\voidb@x
  665.    \fi
  666. \endprog
  667. \sect To restore a page during the second output invocation, we first
  668. restore the saved values, but only if they were really changed (this can
  669. be discovered by the value of |\cb_save_vsize|).  Now the |\cb_save_page| is
  670. appended to the current list as a box, which stops later usage of its
  671. stretch- and shrinkability!  Then the collected insertions can be
  672. inserted again. The page marks have to be inserted, too.
  673. \beginprog
  674. \def\cb_restore_page{%
  675.    \ifdim \cb_save_vsize=\maxdimen
  676.    \else  \global\vsize=\cb_save_vsize
  677.      \global\dimen\footins=\cb_save_dimen_footins
  678.      \global\cb_save_vsize=\maxdimen
  679.      \cb_restore_page_marks
  680.    \fi
  681.    \box\cb_save_page % discards stretch- and shrinkability!
  682.    \ifvoid \footins
  683.    \else  \insert\footins{\floatingpenalty=20000 \unvbox\footins}%
  684.    \fi
  685. \endprog
  686. \sect {\em Please note, that there is still a problem with this concept 
  687. of handling the output trigger:}
  688. \bigskip
  689. If the first output trigger is discarded because a page break has occurred 
  690. just in front, footnote parts may be juggled around. I.e., if a 
  691. footnote is split in three parts, the first part was just been shipped 
  692. out, the second part is inserted back into the recent contributions by 
  693. the output routine but {\em behind\/} the third part which is saved in 
  694. the ``special place'' (according to the \TeX{}book, p.~125). A solution to
  695. this problem might be to insert a |\do_change| again within the second
  696. output trigger and finishing the treatment afterwards. Afterwards a 
  697. full triggering process (two output invocations) is executed again and 
  698. all insertion parts will be accessible in the insertion box.
  699. By the way, the almost same problem appears in \LaTeX{}, too. Almost: in
  700. \LaTeX{} this can happen every time because at the first output invocation
  701. the |\dimen|-values of the footnote insertion is not increased. I leave 
  702. the problem open to the reader\,\dots
  703. \chap Writing the Stuff.
  704. The positions of the bars which mark the changed areas are relative to
  705. the top of the text, i.e.\ the height of the top insertion is not
  706. included.  Therefore it is best to write them just after the top
  707. insertions before the page text---but to do this we have to change the
  708. \beginchange
  709. either the {\sc Plain} {\TeX} macro |\pagecontents| or the {\LaTeX}
  710. macro |\@makecol|.
  711. \endchange
  712. Below is the new definition, I have just rearranged it a little bit so
  713. that it is more legible.  The new lines have been marked with
  714. `|%%%%|'.  |\cb_insert_current_bar| inserts a last element in |\cb_bar_list|
  715. if a changed area is not yet finished, afterwards all bars can be
  716. written.
  717. \beginprog
  718. \def\@makecol{%
  719.    \ifvoid\footins 
  720.      \setbox\@outputbox
  721.        \vbox{
  722.           %\typeout{write all bars -- no footins}     
  723.           \cb_insert_current_bar \cb_write_all_bars %%%%
  724.           \unvbox\@cclv%
  725.             }
  726.    \else
  727.       \setbox\@outputbox
  728.         \vbox{\boxmaxdepth \maxdepth
  729.           %\typeout{write all bars}     
  730.           \cb_insert_current_bar \cb_write_all_bars %%%%
  731.         \unvbox\@cclv\vskip\skip\footins\footnoterule\unvbox\footins%
  732.              }
  733.    \fi%
  734.    \xdef\@freelist{\@freelist\@midlist}\gdef\@midlist{}\@combinefloats
  735.    \setbox\@outputbox\vbox to\@colht{\boxmaxdepth\maxdepth
  736.       \@texttop\dimen128=\dp\@outputbox\unvbox\@outputbox
  737.       \vskip-\dimen128\@textbottom}%
  738.    \global\maxdepth\@maxdepth}
  739. \endprog
  740. \sect If $|\cb_top_change_pos|=|\maxdimen|$ no change is active.  Otherwise
  741. the current change reaches from the begin mark (|\cb_top_change_pos|) to
  742. the end of the page, i.e.\ we insert a virtual end mark.  Because the
  743. change continues on the next page we insert a virtual begin mark on the
  744. top of the page, too.
  745. \beginprog
  746. \def\cb_insert_current_bar{%
  747.    \ifdim \cb_top_change_pos=\maxdimen
  748.    \else%
  749.       \cb_bot_change_pos=\ht\@cclv
  750.       \advance\cb_bot_change_pos by \dp\@cclv
  751.       \xdef\cb_bar_list{\cb_bar_list 
  752.                           \cb_bar(\the\cb_top_change_pos,
  753.                                   \the\cb_bot_change_pos,
  754.                                   \the\changebarwidth)}%
  755.       \global\cb_top_change_pos=0pt
  756.    \fi%
  757. \endprog
  758. \sect Now we can write all bars---if they exist anyway.  It's rather
  759. easy, we just have to define |\cb_bar| to |\cb_write_bar| and execute
  760. |\cb_bar_list|.  The resulting output must not use vertical place.  We must
  761. not forget to delete the list, or we will get the same bars on the next
  762. page again.
  763. \beginprog
  764. \newbox\cb_bars
  765. \newdimen\cb_offset
  766. \def\cb_write_all_bars{%
  767.    \ifx \cb_bar_list\empty
  768.    \else                           % changes exist
  769.       \ifchbarRight
  770.         \cb_offset = \hsize
  771.         \advance \cb_offset by \BarDistance
  772.       \else
  773.         \cb_offset = \hoffset  
  774.         \advance \cb_offset by -\BarDistance
  775.       \fi
  776.       \setbox\cb_bars=\hbox to \cb_offset{%
  777.          \hskip\cb_offset
  778.          \vbox to 0pt{\offinterlineskip
  779.             \let\cb_bar=\cb_write_bar  \cb_bar_list
  780.             }%
  781.          \hss
  782.          }%
  783.       \ht\cb_bars=0pt \dp\cb_bars=0pt \box\cb_bars
  784.       \global\let\cb_bar_list=\empty
  785.    \fi
  786. \endprog
  787. \beginchange\chap Cleaning Up.\endchange
  788. We finish the macro file so that garbage (e.g.\ of exchanges
  789. between systems) can come afterwards.
  790. \beginprog
  791. \catcode`\@=\atcode
  792. \catcode`\_=\uscode
  793. \endinput
  794. \endprog
  795. \end{document}
  796.